<template>
	<div :class="{
      'tui-chat': true,
      'tui-chat-h5': isMobile,
    }" @click="onMessageListBackgroundClick">
		<!-- <JoinGroupCard /> -->
		<div class="tui-chat-main">
			<div v-if="isOfficial" class="tui-chat-safe-tips">
				<span>
					{{
            TUITranslateService.t(
              "TUIChat.【安全提示】本 APP 仅用于体验腾讯云即时通信 IM 产品功能，不可用于业务洽谈与拓展。请勿轻信汇款、中奖等涉及钱款的信息，勿轻易拨打陌生电话，谨防上当受骗。"
            )
          }}
				</span>
				<a @click="openComplaintLink(Link.complaint)">{{
          TUITranslateService.t("TUIChat.点此投诉")
        }}</a>
			</div>
			<MessageGroupApplication v-if="isGroup" :key="props.groupID" :groupID="props.groupID" />
			<scroll-view id="messageScrollList" class="tui-message-list" scroll-y="true" :scroll-top="scrollTop"
				:scroll-into-view="`tui-${historyFirstMessageID}`" @scroll="handelScrollListScroll">
				<p v-if="!isCompleted" class="message-more" @click="getHistoryMessageList">
					{{ TUITranslateService.t("TUIChat.查看更多") }}
				</p>
				<li v-for="(item, index) in messageList" :id="`tui-${item.ID}`" :key="item.vueForRenderKey"
					:class="'message-li ' + item.flow">
					<MessageTimestamp :currTime="item.time" :prevTime="index > 0 ? messageList[index - 1].time : 0" />
					<div class="message-item" @click="toggleID = ''">
						<MessageTip v-if="item.type === TYPES.MSG_GRP_TIP ||
                isCreateGroupCustomMessage(item)
              " :content="item.getMessageContent()" />
						<!-- @click="setCurrentTab(item)" -->
						<div v-else-if="!item.isRevoked && !isPluginMessage(item)" :id="`msg-bubble-${item.ID}`"
							class="message-bubble-container"
							@longpress="handleToggleMessageItem($event, item, index, true)"
							@touchstart="handleH5LongPress($event, item, index, 'touchstart')"
							@touchend="handleH5LongPress($event, item, index, 'touchend')"
							@mouseover="handleH5LongPress($event, item, index, 'touchend')">
							<MessageBubble :messageItem="deepCopy(item)" :content="item.getMessageContent()"
								:isAudioPlayed="audioPlayedMapping[item.ID]" :blinkMessageIDList="blinkMessageIDList"
								:isMultipleSelectMode="isMultipleSelectMode" :groupID="props.groupID"
								:multipleSelectedMessageIDList="multipleSelectedMessageIDList"
								@resendMessage="resendMessage(item)" @blinkMessage="blinkMessage" @scrollTo="scrollTo"
								@changeSelectMessageIDList="changeSelectMessageIDList"
								@setReadReceiptPanelVisible="setReadReceiptPanelVisible"
								@longPressAvatar="emits('longPressAvatar', $event)">
								<MessageText v-if="item.type === TYPES.MSG_TEXT" :content="item.getMessageContent()" />
								<ProgressMessage v-else-if="item.type === TYPES.MSG_IMAGE"
									:content="item.getMessageContent()" :messageItem="deepCopy(item)">
									<MessageImage :content="item.getMessageContent()" :messageItem="item"
										@previewImage="handleImagePreview(index)" />
								</ProgressMessage>
								<ProgressMessage v-else-if="item.type === TYPES.MSG_VIDEO"
									:content="item.getMessageContent()" :messageItem="deepCopy(item)">
									<MessageVideo :content="item.getMessageContent()" :messageItem="item" />
								</ProgressMessage>
								<MessageAudio v-else-if="item.type === TYPES.MSG_AUDIO"
									:content="item.getMessageContent()" :messageItem="item"
									:broadcastNewAudioSrc="broadcastNewAudioSrc" @setAudioPlayed="setAudioPlayed"
									@getGlobalAudioContext="getGlobalAudioContext" />
								<MessageRecord v-else-if="item.type === TYPES.MSG_MERGER" :renderData="item.payload"
									:messageItem="item" @assignMessageIDInUniapp="assignMessageIDInUniapp" />
								<MessageFile v-else-if="item.type === TYPES.MSG_FILE"
									:content="item.getMessageContent()" />
								<MessageFace v-else-if="item.type === TYPES.MSG_FACE"
									:content="item.getMessageContent()" />
								<MessageLocation v-else-if="item.type === TYPES.MSG_LOCATION"
									:content="item.getMessageContent()" />
								<MessageCustom v-else-if="item.type === TYPES.MSG_CUSTOM"
									:content="item.getMessageContent()" :messageItem="item" />
							</MessageBubble>
						</div>
						<MessagePlugin v-else-if="!item.isRevoked && isPluginMessage(item)" :message="item"
							@resendMessage="resendMessage" @handleToggleMessageItem="handleToggleMessageItem"
							@handleH5LongPress="handleH5LongPress" />
						<MessageRevoked v-else :isEdit="item.type === TYPES.MSG_TEXT" :messageItem="item"
							@messageEdit="handleEdit(item)" />
						<!-- message tool -->
						<MessageTool v-if="item.ID === toggleID" :class="{
                'message-tool': true,
                'message-tool-out': item.flow === 'out',
                'message-tool-in': item.flow === 'in',
              }" :messageItem="item" :isMultipleSelectMode="isMultipleSelectMode"
							@toggleMultipleSelectMode="() => emits('toggleMultipleSelectMode')" />
					</div>
				</li>
			</scroll-view>
			<!-- scroll button -->
			<ScrollButton ref="scrollButtonInstanceRef" @scrollToLatestMessage="scrollToLatestMessage" />
			<Dialog v-if="reSendDialogShow" :show="reSendDialogShow" :isH5="!isPC" :center="true" :isHeaderShow="isPC"
				@submit="resendMessageConfirm()" @update:show="(e) => (reSendDialogShow = e)">
				<p class="delDialog-title">
					{{ TUITranslateService.t("TUIChat.确认重发该消息？") }}
				</p>
			</Dialog>
			<!-- read receipt panel -->
			<ReadReceiptPanel v-if="isShowReadUserStatusPanel" :message="Object.assign({}, readStatusMessage)"
				@setReadReceiptPanelVisible="setReadReceiptPanelVisible" />
			<!-- simple message list -->
			<Drawer :visible="isShowSimpleMessageList" :overlayColor="'transparent'" :popDirection="'right'">
				<SimpleMessageList :style="{height: '100%'}" :isMounted="isShowSimpleMessageList"
					:messageID="simpleMessageListRenderMessageID" @closeOverlay="isShowSimpleMessageList = false" />
			</Drawer>
		</div>
		<ContactInfo @switchConversation="switchConversation" />
	</div>
</template>

<script lang="ts" setup>
	import {
		ref,
		watch,
		nextTick,
		onMounted,
		onUnmounted,
		getCurrentInstance,
	} from '../../../adapter-vue';
	import TUIChatEngine, {
		IMessageModel,
		TUIGroupService,
		TUIStore,
		StoreName,
		TUITranslateService,
		TUIChatService,
	} from '@tencentcloud/chat-uikit-engine';
	import {
		setInstanceMapping,
		getBoundingClientRect,
		getScrollInfo,
	} from '@tencentcloud/universal-api';
	// import { JoinGroupCard } from '@tencentcloud/call-uikit-wechat';
	import Link from './link';
	import SimpleMessageList from './message-elements/simple-message-list/index.vue';
	import MessageGroupApplication from './message-group-application/index.vue';
	import MessageText from './message-elements/message-text.vue';
	import MessageImage from './message-elements/message-image.vue';
	import MessageAudio from './message-elements/message-audio.vue';
	import MessageRecord from './message-elements/message-record/index.vue';
	import MessageFile from './message-elements/message-file.vue';
	import MessageFace from './message-elements/message-face.vue';
	import MessageCustom from './message-elements/message-custom.vue';
	import MessageTip from './message-elements/message-tip.vue';
	import MessageBubble from './message-elements/message-bubble.vue';
	import MessageLocation from './message-elements/message-location.vue';
	import MessageTimestamp from './message-elements/message-timestamp.vue';
	import MessageVideo from './message-elements/message-video.vue';
	import MessageTool from './message-tool/index.vue';
	import MessageRevoked from './message-tool/message-revoked.vue';
	import MessagePlugin from '../../../plugins/plugin-components/message-plugin.vue';
	import ReadReceiptPanel from './read-receipt-panel/index.vue';
	import ScrollButton from './scroll-button/index.vue';
	import { isPluginMessage } from '../../../plugins/plugin-components/index';
	import Dialog from '../../common/Dialog/index.vue';
	import Drawer from '../../common/Drawer/index.vue';
	import { Toast, TOAST_TYPE } from '../../common/Toast/index';
	import ProgressMessage from '../../common/ProgressMessage/index.vue';
	import { isCreateGroupCustomMessage } from '../utils/utils';
	import { isEnabledMessageReadReceiptGlobal, deepCopy } from '../utils/utils';
	import { throttle } from '../../../utils/lodash';
	import { isPC, isH5, isMobile } from '../../../utils/env';
	import chatStorage from '../utils/chatStorage';
	import { IAudioContext } from '../../../interface';
	import ContactInfo from '@/TUIKit/components/TUIContact/contact-info/index.vue';
	import { TUIGlobal } from '@tencentcloud/universal-api';

	interface IEmits {
		(e : 'closeInputToolBar') : void;
		(e : 'handleEditor', message : IMessageModel, type : string) : void;
		(key : 'toggleMultipleSelectMode') : void;
		(e : 'longPressAvatar', info : { id : string; label : string; }) : void; //新增长按头像@-
	}

	interface IProps {
		isGroup : boolean;
		groupID : string;
		isNotInGroup : boolean;
		isMultipleSelectMode : boolean;
	}






	// 点击成员头像跳转到成员信息页面
	const currentContactInfo = ref<IContactInfoType>({} as IContactInfoType);
	function setCurrentTab(item) {
		let objvalue = {
			remark: "",
			renderKey: "friendList-30000",
			source: "AddSource_Type_Web",
			userID: item.from,
		}
		currentContactInfo.value = objvalue;
		TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', currentContactInfo.value);
	}
	// const emits = defineEmits(['switchConversation']);
	const switchConversation = (data : any) => {
		TUIGlobal?.navigateTo({
			url: '/TUIKit/components/TUIChat/index',
		});
		emits('switchConversation', data);
	};


	const emits = defineEmits<IEmits>();
	const props = withDefaults(defineProps<IProps>(), {
		isGroup: false,
		groupID: '',
		isNotInGroup: false,
		isMultipleSelectMode: false,
	});

	let selfAddValue = 0;
	let observer : any = null;
	let groupType : string | undefined;
	const sentReceiptMessageID = new Set<string>();
	const isOfficial = TUIStore.getData(StoreName.APP, 'isOfficial');
	const thisInstance = getCurrentInstance()?.proxy || getCurrentInstance();

	const messageList = ref<IMessageModel[]>();
	const multipleSelectedMessageIDList = ref<string[]>([]);
	const isCompleted = ref(false);
	const currentConversationID = ref('');
	const toggleID = ref('');
	const scrollTop = ref(5000); // The initial number of messages is 15, and the maximum message height is 300.
	const TYPES = ref(TUIChatEngine.TYPES);
	const isLoadingMessage = ref(false);
	const isLongpressing = ref(false);
	const blinkMessageIDList = ref<string[]>([]);
	const messageTarget = ref<IMessageModel>();
	const scrollButtonInstanceRef = ref<InstanceType<typeof ScrollButton>>();
	const historyFirstMessageID = ref<string>('');
	const isShowSimpleMessageList = ref<boolean>(false);
	const simpleMessageListRenderMessageID = ref<string>();
	const audioPlayedMapping = ref<Record<string, boolean>>({});

	// audio control
	const broadcastNewAudioSrc = ref<string>('');

	const readStatusMessage = ref<IMessageModel>();
	const isShowReadUserStatusPanel = ref<boolean>(false);

	// Resend Message Dialog
	const reSendDialogShow = ref(false);
	const resendMessageData = ref();

	const scrollToBottom = () => {
		scrollTop.value += 300;
		// Solve the issue where swiping to the bottom for the first time after packaging Uniapp into an app has a delay,
		// which can be set to 300 ms.
		const timer = setTimeout(() => {
			scrollTop.value += 1;
			clearTimeout(timer);
		}, 300);
	};

	const onCurrentConversationIDUpdated = (conversationID : string) => {
		currentConversationID.value = conversationID;
		if (isEnabledMessageReadReceiptGlobal()) {
			const { groupProfile }
				= TUIStore.getConversationModel(conversationID) || {};
			groupType = groupProfile?.type;
		}

		if (Object.keys(audioPlayedMapping.value).length > 0) {
			// Synchronize storage about whether the audio has been played when converstaion switched
			chatStorage.setChatStorage('audioPlayedMapping', audioPlayedMapping.value);
		}
	};

	onMounted(() => {
		// Retrieve the information about whether the audio has been played from localStorage
		audioPlayedMapping.value = chatStorage.getChatStorage('audioPlayedMapping') || {};

		TUIStore.watch(StoreName.CHAT, {
			messageList: onMessageListUpdated,
			messageSource: onMessageSourceUpdated,
			isCompleted: onChatCompletedUpdated,
		});

		TUIStore.watch(StoreName.CONV, {
			currentConversationID: onCurrentConversationIDUpdated,
		});

		setInstanceMapping('messageList', thisInstance);

		uni.$on('scroll-to-bottom', scrollToLatestMessage);
	});

	onUnmounted(() => {
		TUIStore.unwatch(StoreName.CHAT, {
			messageList: onMessageListUpdated,
			isCompleted: onChatCompletedUpdated,
		});

		TUIStore.unwatch(StoreName.CONV, {
			currentConversationID: onCurrentConversationIDUpdated,
		});

		observer?.disconnect();
		observer = null;

		uni.$off('scroll-to-bottom');

		if (Object.keys(audioPlayedMapping.value).length > 0) {
			// Synchronize storage about whether the audio has been played when the component is unmounted
			chatStorage.setChatStorage('audioPlayedMapping', audioPlayedMapping.value);
		}
	});

	const handelScrollListScroll = throttle(
		function (e : Event) {
			scrollButtonInstanceRef.value?.judgeScrollOverOneScreen(e);
		},
		500,
		{ leading: true },
	);

	function getGlobalAudioContext(
		audioMap : Map<string, IAudioContext>,
		options ?: { newAudioSrc : string },
	) {
		if (options?.newAudioSrc) {
			broadcastNewAudioSrc.value = options.newAudioSrc;
		}
	}

	async function onMessageListUpdated(list : IMessageModel[]) {
		observer?.disconnect();
		messageList.value = list
			.filter(message => !message.isDeleted)
			.map((message) => {
				message.vueForRenderKey = `${message.ID}`;
				return message;
			});
		const newLastMessage = messageList.value?.[messageList.value?.length - 1];
		if (messageTarget.value) {
			// scroll to target message
			scrollAndBlinkMessage(messageTarget.value);
		} else if (!isLoadingMessage.value && !(scrollButtonInstanceRef.value?.isScrollButtonVisible && newLastMessage?.flow === 'in')) {
			// scroll to bottom
			nextTick(() => {
				scrollToBottom();
			});
		}
		if (isEnabledMessageReadReceiptGlobal()) {
			nextTick(() => bindIntersectionObserver());
		}
	}

	async function scrollToLatestMessage() {
		try {
			const { scrollHeight } = await getScrollInfo(
				'#messageScrollList',
				'messageList',
			);
			if (scrollHeight) {
				scrollTop.value === scrollHeight
					? (scrollTop.value = scrollHeight + 1)
					: (scrollTop.value = scrollHeight);
			} else {
				scrollToBottom();
			}
		} catch (error) {
			scrollToBottom();
		}
	}

	async function onMessageSourceUpdated(message : IMessageModel) {
		messageTarget.value = message;
		scrollAndBlinkMessage(messageTarget.value);
	}

	function scrollAndBlinkMessage(message : IMessageModel) {
		if (
			messageList.value?.some(
				messageListItem => messageListItem?.ID === message?.ID,
			)
		) {
			nextTick(async () => {
				await scrollToTargetMessage(message);
				await blinkMessage(message?.ID);
				messageTarget.value = undefined;
			});
		}
	}

	function onChatCompletedUpdated(flag : boolean) {
		isCompleted.value = flag;
	}

	const getHistoryMessageList = () => {
		isLoadingMessage.value = true;
		const currentFirstMessageID = messageList.value?.[0]?.ID || '';
		TUIChatService.getMessageList().then(() => {
			nextTick(() => {
				historyFirstMessageID.value = currentFirstMessageID;
				const timer = setTimeout(() => {
					historyFirstMessageID.value = '';
					isLoadingMessage.value = false;
					clearTimeout(timer);
				}, 500);
			});
		});
	};

	const openComplaintLink = () => { };

	// toggle message
	const handleToggleMessageItem = (
		e : any,
		message : IMessageModel,
		index : number,
		isLongpress = false,
	) => {
		if (props.isMultipleSelectMode || props.isNotInGroup) {
			return;
		}
		if (isLongpress) {
			isLongpressing.value = true;
		}
		toggleID.value = message.ID;
	};

	// h5 long press
	let timer : number;
	const handleH5LongPress = (
		e : any,
		message : IMessageModel,
		index : number,
		type : string,
	) => {
		if (props.isMultipleSelectMode || props.isNotInGroup) {
			return;
		}
		if (!isH5) return;
		function longPressHandler() {
			clearTimeout(timer);
			handleToggleMessageItem(e, message, index, true);
		}
		function touchStartHandler() {
			timer = setTimeout(longPressHandler, 500);
		}
		function touchEndHandler() {
			clearTimeout(timer);
		}
		switch (type) {
			case 'touchstart':
				touchStartHandler();
				break;
			case 'touchend':
				touchEndHandler();
				setTimeout(() => {
					isLongpressing.value = false;
				}, 200);
				break;
		}
	};

	// reedit message
	const handleEdit = (message : IMessageModel) => {
		emits('handleEditor', message, 'reedit');
	};

	const resendMessage = (message : IMessageModel) => {
		reSendDialogShow.value = true;
		resendMessageData.value = message;
	};

	const handleImagePreview = (index : number) => {
		if (!messageList.value) {
			return;
		}
		const imageMessageIndex : number[] = [];
		const imageMessageList : IMessageModel[] = messageList.value.filter((item, index) => {
			if (
				!item.isRevoked
				&& !item.hasRiskContent
				&& item.type === TYPES.value.MSG_IMAGE
			) {
				imageMessageIndex.push(index);
				return true;
			}
			return false;
		});
		uni.previewImage({
			current: imageMessageIndex.indexOf(index),
			urls: imageMessageList.map(message => message.payload.imageInfoArray?.[2].url),
			// #ifdef APP-PLUS
			indicator: 'number',
			// #endif
		});
	};

	const resendMessageConfirm = () => {
		reSendDialogShow.value = !reSendDialogShow.value;
		const messageModel = resendMessageData.value;
		messageModel.resendMessage();
	};

	function blinkMessage(messageID : string) : Promise<void> {
		return new Promise((resolve) => {
			const index = blinkMessageIDList.value.indexOf(messageID);
			if (index < 0) {
				blinkMessageIDList.value.push(messageID);
				const timer = setTimeout(() => {
					blinkMessageIDList.value.splice(
						blinkMessageIDList.value.indexOf(messageID),
						1,
					);
					clearTimeout(timer);
					resolve();
				}, 3000);
			}
		});
	}

	function scrollTo(scrollHeight : number) {
		scrollTop.value = scrollHeight;
	}


	async function bindIntersectionObserver() {
		if (!messageList.value || messageList.value.length === 0) {
			return;
		}

		if (
			groupType === TYPES.value.GRP_AVCHATROOM
			|| groupType === TYPES.value.GRP_COMMUNITY
		) {
			// AVCHATROOM and COMMUNITY chats do not monitor read receipts for messages.
			return;
		}

		observer?.disconnect();
		observer = uni
			.createIntersectionObserver(thisInstance, {
				threshold: [0.7],
				observeAll: true,
				// In Uni-app, the `safetip` is also included, so a negative margin is needed to exclude it.
			})
			.relativeTo('#messageScrollList', { top: -70 });
		// .relativeTo('#messageScrollList', { top: -70 }); bottom: 50 threshold: [0.7],

		observer?.observe('.message-li.in .message-bubble-container', (res : any) => {
			console.log('<<<触发回执', res)
			if (sentReceiptMessageID.has(res.id)) {
				return;
			}
			const matchingMessage = messageList.value.find((message : IMessageModel) => {
				return res.id.indexOf(message.ID) > -1;
			});
			if (
				matchingMessage
				&& matchingMessage.needReadReceipt
				&& matchingMessage.flow === 'in'
				&& !matchingMessage.readReceiptInfo?.isPeerRead
			) {
				console.log('<<<执行-回执-接收消息触发', matchingMessage.payload.text)
				TUIChatService.sendMessageReadReceipt([matchingMessage]);
				sentReceiptMessageID.add(res.id);
			}
		});
	}

	function setReadReceiptPanelVisible(visible : boolean, message ?: IMessageModel) {
		if (visible && props.isNotInGroup) {
			return;
		}
		if (!visible) {
			readStatusMessage.value = undefined;
		} else {
			readStatusMessage.value = message;
		}
		isShowReadUserStatusPanel.value = visible;
	}

	async function scrollToTargetMessage(message : IMessageModel) {
		const targetMessageID = message.ID;
		const isTargetMessageInScreen
			= messageList.value
			&& messageList.value.some(msg => msg.ID === targetMessageID);
		if (targetMessageID && isTargetMessageInScreen) {
			const timer = setTimeout(async () => {
				try {
					const scrollViewRect = await getBoundingClientRect(
						'#messageScrollList',
						'messageList',
					);
					const originalMessageRect = await getBoundingClientRect(
						'#tui-' + targetMessageID,
						'messageList',
					);
					const { scrollTop } = await getScrollInfo(
						'#messageScrollList',
						'messageList',
					);
					const finalScrollTop
						= originalMessageRect.top
						+ scrollTop
						- scrollViewRect.top
						- (selfAddValue++ % 2);
					scrollTo(finalScrollTop);
					clearTimeout(timer);
				} catch (error) {
					// todo
				}
			}, 500);
		} else {
			Toast({
				message: TUITranslateService.t('TUIChat.无法定位到原消息'),
				type: TOAST_TYPE.WARNING,
			});
		}
	}

	function onMessageListBackgroundClick() {
		emits('closeInputToolBar');
	}

	watch(() => props.isMultipleSelectMode, (newValue) => {
		if (!newValue) {
			changeSelectMessageIDList({
				type: 'clearAll',
				messageID: '',
			});
		}
	});

	function changeSelectMessageIDList({ type, messageID } : { type : 'add' | 'remove' | 'clearAll'; messageID : string }) {
		// TODO need to delete this
		if (type === 'clearAll') {
			multipleSelectedMessageIDList.value = [];
		} else if (type === 'add' && !multipleSelectedMessageIDList.value.includes(messageID)) {
			multipleSelectedMessageIDList.value.push(messageID);
		} else if (type === 'remove') {
			multipleSelectedMessageIDList.value = multipleSelectedMessageIDList.value.filter(id => id !== messageID);
		}
	}

	function mergeForwardMessage() {
		TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
			isMergeForward: true,
			messageIDList: multipleSelectedMessageIDList.value,
		});
	}

	function oneByOneForwardMessage() {
		TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
			isMergeForward: false,
			messageIDList: multipleSelectedMessageIDList.value,
		});
	}

	function assignMessageIDInUniapp(messageID : string) {
		simpleMessageListRenderMessageID.value = messageID;
		isShowSimpleMessageList.value = true;
	}

	function setAudioPlayed(messageID : string) {
		audioPlayedMapping.value[messageID] = true;
	}

	defineExpose({
		oneByOneForwardMessage,
		mergeForwardMessage,
		scrollToLatestMessage,
		scrollAndBlinkMessage,//@悬浮提醒功能-滚动至对应消息并提示
	});
</script>
<style lang="scss" scoped src="./style/index.scss"></style>